package folder.main.exp1;

import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.math3.distribution.TDistribution;
import org.apache.commons.math3.stat.descriptive.SummaryStatistics;

import folder.main.exp1.algorithms.Algorithm1;
import folder.main.exp1.algorithms.Special;
import folder.main.exp1.algorithms.Balanced;
import folder.main.exp1.algorithms.Normal;
import folder.main.exp1.algorithms.Random;
import folder.model.network.Network;
import folder.model.network.NetworkPaths;
import folder.model.network.NetworkRandom;
import folder.model.node.Node;

public class Exp1_JITel
{	
	static int NUMBER_NODES = 20;
	static int NUMBER_LINKS = 90;	// Min 30
	
	static String NAME_FILE_1 = "C:/Users/aurelioj/Google Drive/R/Experimento/File1.txt";
	static String NAME_FILE_2 = "C:/Users/aurelioj/Google Drive/R/Experimento/File2.txt";
	static String NAME_FILE_3 = "C:/Users/aurelioj/Google Drive/R/Experimento/File3.txt";
	
	static int NUMBER_ALGORITHMS = 4;
	static int NUMBER_EVALUATIONS = 100;
	static int NUMBER_SPECIAL_NODES = 6;
	
	static SummaryStatistics sumDelayToNodeTargetMean = new SummaryStatistics();
	
	static List < SummaryStatistics > sumDelayMeanSpecial = new ArrayList< SummaryStatistics >();
	static List < SummaryStatistics > sumDelayMeanNormal = new ArrayList< SummaryStatistics >();
	static List < SummaryStatistics > sumDifferenceDelays = new ArrayList< SummaryStatistics >();
	
	public static void main( String[] args )
	{
		initEvaluation();
		
		int numberEvaluation = 0;
		
		while( numberEvaluation < NUMBER_EVALUATIONS )
		{
			Network network = NetworkRandom.networkRandom( NUMBER_NODES, NUMBER_LINKS );
			
						
			List< Algorithm1 > algorithms = runAlgorithms( network );
			
			if( algorithms != null )
			{
				setEvaluation( algorithms );
				setSumDelayToNodeTargetMean( network );
				System.out.println( ++numberEvaluation );
			}
		}
		
		printAndSaveToFile();
	}
	
	static void initEvaluation()
	{
		for( int i = 0; i < NUMBER_ALGORITHMS; i++ )
		{
			sumDelayMeanSpecial.add( new SummaryStatistics() );
			sumDelayMeanNormal.add( new SummaryStatistics() );
			sumDifferenceDelays.add( new SummaryStatistics() );
		}
	}
	
	static void setEvaluation( List< Algorithm1 > algorithms )
	{
		for( int j = 0; j < algorithms.size(); j++  )
		{
			Algorithm1 algorithm = algorithms.get( j );
			sumDelayMeanSpecial.get( j ).addValue( algorithm.getDelayMeanSpecial() );
			sumDelayMeanNormal.get( j ).addValue( algorithm.getDelayMeanNormal() );
			sumDifferenceDelays.get( j ).addValue( algorithm.getDifferenceDelays() );
		}
	}
	
	static void setSumDelayToNodeTargetMean( Network networkPath )
	{
		SummaryStatistics sum = new SummaryStatistics();
		
		for( Node node: networkPath.getNodes() )
			sum.addValue( node.getDelayToNodeTargetMean() );
		
		sumDelayToNodeTargetMean.addValue( sum.getMean() );
	}
	
	static List< Algorithm1 > runAlgorithms( Network networkPath )
	{
		List< Algorithm1 > algorithms = new ArrayList< Algorithm1 >();
		
		Algorithm1 algorithm = Special.special( networkPath, NUMBER_SPECIAL_NODES );
		
		if( algorithm.getCheckDelays() )
			algorithms.add( algorithm );
		else
			return null;
					
		algorithm = Normal.normal( networkPath, NUMBER_SPECIAL_NODES );
		
		if( algorithm.getCheckDelays() )
			algorithms.add( algorithm );
		else
			return null;
		
		algorithm = Balanced.balanced( networkPath, NUMBER_SPECIAL_NODES );
		
		if( algorithm.getCheckDelays() )
			algorithms.add( algorithm );
		else
			return null;
		
		algorithm = Random.random( networkPath, NUMBER_SPECIAL_NODES );
		
		if( algorithm.getCheckDelays() )
			algorithms.add( algorithm );
		else
			return null;
		
		return algorithms;
	}
	
	static void printAndSaveToFile()
	{
		DecimalFormatSymbols decimalFormatSymbols = new DecimalFormatSymbols();
		decimalFormatSymbols.setDecimalSeparator( '.' );
		
		DecimalFormat decimalFormat = new DecimalFormat( "0.0", decimalFormatSymbols );
		
		double factor = 1000000;
		double delayToNodeTargetMean = sumDelayToNodeTargetMean.getMean() * factor;
		
		String stringDelayToNodeTargetMean = decimalFormat.format( delayToNodeTargetMean );
		System.out.println( "Mean delay to each node: " + stringDelayToNodeTargetMean );
		
		String dataToAddToFile1 = "";
		String dataToAddToFile2 = "";
		String dataToAddToFile3 = "";
		
		for( int i = 0; i < NUMBER_ALGORITHMS; i++  )
		{
			String data1 = getStringOfData( sumDelayMeanSpecial.get( i ), decimalFormat, factor );
			String data2 = getStringOfData( sumDelayMeanNormal.get( i ), decimalFormat, factor );
			String data3 = getStringOfData( sumDifferenceDelays.get( i ), decimalFormat, factor );
			
			System.out.println( "\t\t\t\t\t\t\t" + "Algorithm " + i );
			System.out.println( "Mean delay from special nodes to each normal node: \t" + data1 );
			System.out.println( "Mean delay from normal nodes to each normal node: \t" + data2 );
			System.out.println( "Difference of mean delays: \t\t\t\t" + data3 + "\n" );
			
			dataToAddToFile1 += data1;
			dataToAddToFile2 += data2;
			dataToAddToFile3 += data3;
		}
		
		dataToAddToFile1 += stringDelayToNodeTargetMean;
		dataToAddToFile2 += stringDelayToNodeTargetMean;
		
		addDataToFile( dataToAddToFile1, NAME_FILE_1 );
		addDataToFile( dataToAddToFile2, NAME_FILE_2 );
		addDataToFile( dataToAddToFile3, NAME_FILE_3 );
	}
	
	static void addDataToFile( String data, String fileName )
	{
		try {
			FileWriter fileWriter = new FileWriter( fileName, true );
			PrintWriter printWriter = new PrintWriter( fileWriter );
			printWriter.println( data );
			printWriter.close();
			
		} catch (IOException e) {e.printStackTrace();}
	}
	
	static String getStringOfData( SummaryStatistics sum, DecimalFormat decimalFormat, double factor )
	{
		double mean = sum.getMean();
		double width = getConfidenceIntervalWidth( sum, 0.05 );
		return ( decimalFormat.format( mean * factor ) ) + "\t" + decimalFormat.format( ( mean - width ) * factor ) + "\t" + decimalFormat.format( ( mean + width ) * factor ) + "\t";
	}
	
	static double getConfidenceIntervalWidth( SummaryStatistics sum, double significance ) //throws MathException
	{
		if( sum.getN() == 0 )
			return 0;
		
		long N = sum.getN() > 1 ? sum.getN() : 2;
				
		TDistribution tDist = new TDistribution( N - 1 );
		double a = tDist.inverseCumulativeProbability( 1.0 - significance / 2 );
		return a * sum.getStandardDeviation() / Math.sqrt( sum.getN() );
	}
		    
}
